using Domain.Core;
using NUnit.Framework;

namespace Domain.Core.Tests
{
    // TODO: Refactor these tests.
    /// <summary>
    /// Tests the <see cref="Money"/> and <see cref="MoneyCurrency"/> structs.
    /// </summary>
    /// <remarks>
    /// Tests provided by author of code: http://decav.com/blogs/andre/archive/2006/09/06/433.aspx.
    /// </remarks>
    [TestFixture]
    public class MoneyTest
    {
        [Test]
        public void TestCurrency()
        {
            // Test getting currencies by short code.
            MoneyCurrency usdcur1 = MoneyCurrency.GetCurrency("usd");
            Assert.AreEqual(usdcur1, MoneyCurrency.USDollar, "The Get MoneyCurrency by ISO short-code (3 letter) test failed.");

            // Test getting from ISO code (int).
            MoneyCurrency usdcur2 = MoneyCurrency.GetCurrency(840);
            Assert.AreEqual(usdcur2, MoneyCurrency.USDollar, "The Get MoneyCurrency by ISO Integer test failed.");
        }

        #region TestValues

        Money usdZero = new Money(MoneyCurrency.USDollar, 0);
        Money usdPositive5 = new Money(MoneyCurrency.USDollar, 5);
        Money usdPositive10 = new Money(MoneyCurrency.USDollar, 10);
        Money usdPositive15 = new Money(MoneyCurrency.USDollar, 15);
        Money usdPositive25 = new Money(MoneyCurrency.USDollar, 25);
        Money usdNegative5 = new Money(MoneyCurrency.USDollar, -5);
        Money usdNegative10 = new Money(MoneyCurrency.USDollar, -10);
        Money usdNegative15 = new Money(MoneyCurrency.USDollar, -15);

        Money eurZero = new Money(MoneyCurrency.Euro, 0);
        Money eurPositive5 = new Money(MoneyCurrency.Euro, 5);
        Money eurPositive10 = new Money(MoneyCurrency.Euro, 10);
        Money eurPositive15 = new Money(MoneyCurrency.Euro, 15);
        Money eurNegative5 = new Money(MoneyCurrency.Euro, -5);
        Money eurNegative10 = new Money(MoneyCurrency.Euro, -10);
        Money eurNegative15 = new Money(MoneyCurrency.Euro, -15);
        #endregion

        [Test]
        public void TestAddition()
        {
            // Test addition of the same currency.
            Money result = usdPositive10 + usdPositive15;
            Assert.AreEqual(result.Currency, usdPositive10.Currency, "The currency of the sum of two USD currencies are not USD.");
            Assert.AreEqual(25, result.Value, "The addition failed (10+15 != 25).");

            result = usdPositive10 + usdNegative15;
            Assert.AreEqual(usdNegative5, result, "Addition of a large negative did not result in a negative (10+(-15) != -5).");

            result = usdNegative15 + usdPositive10;
            Assert.AreEqual(usdNegative5, result, "Addition of a large negative did not result in a negative ((-15)+10 != -5).");

            // Ensure addition of different currencies is not possible.
            try
            {
                result = usdPositive10 + eurPositive10;
                Assert.Fail("Addition of two different currencies was successful.");
            }
            catch
            {
                // This should have failed, so do nothing.
            }

            try
            {
                result = usdNegative10 + eurPositive10;
                Assert.Fail("Addition of two different currencies was successful.");
            }
            catch
            {
                // This should have failed, so do nothing.
            }

            try
            {
                result = eurPositive10 + usdNegative10;
                Assert.Fail("Addition of two different currencies was successful.");
            }
            catch
            {
                // This should have failed, so do nothing.
            }

            // Ensure zero can always be added.
            try
            {
                result = eurPositive10 + eurZero;
                Assert.AreEqual(eurPositive10, result, "EUR10+EUR0 != EUR10");

                result = eurZero + eurPositive10;
                Assert.AreEqual(eurPositive10, result, "EUR0+EUR10 != EUR10");

                result = eurPositive10 + usdZero;
                Assert.AreEqual(eurPositive10, result, "EUR10+USD0 != EUR10");

                result = usdZero + eurPositive10;
                Assert.AreEqual(eurPositive10, result, "USD0+EUR10 != EUR10");

                result = Money.Zero + eurPositive10;
                Assert.AreEqual(eurPositive10, result, "Money.Zero+EUR10 != EUR10");
            }
            catch
            {
                Assert.Fail("Addition of a zero value should work for any currency.");
            }
        }

        [Test]
        public void TestSubtraction()
        {
            Money result = usdPositive15 - usdPositive10;
            Assert.AreEqual(usdPositive5, result, "Subtracting two USD money values resulted in an invalid calculation (15-10 != 5)");

            result = usdPositive15 - usdNegative10;
            Assert.AreEqual(usdPositive25, result, "Subtracting two USD money values resulted in an invalid calculation (15-(-10) != 25)");

            try
            {
                result = usdPositive15 - eurPositive10;
                Assert.Fail("A EUR and USD value should not be able to be subtracted from eachother.");
            }
            catch
            {
                // We expect this calculation to fail.
            }

            // Allow zeros to be added/subtracted from different currencies.
            try
            {
                result = eurPositive10 - eurZero;
                Assert.AreEqual(eurPositive10, result, "EUR10-EUR0 != EUR10");

                result = eurZero - eurPositive10;
                Assert.AreEqual(eurNegative10, result, "EUR0-EUR10 != -EUR10");

                result = eurPositive10 - usdZero;
                Assert.AreEqual(eurPositive10, result, "EUR10-USD0 != EUR10");

                result = usdZero - eurPositive10;
                Assert.AreEqual(eurNegative10, result, "USD0-EUR10 != -EUR10");

                result = Money.Zero - eurPositive10;
                Assert.AreEqual(eurNegative10, result, "Money.Zero-EUR10 != -EUR10");
            }
            catch
            {
                Assert.Fail("Subtraction of a zero value should work for any currency.");
            }

        }

        [Test]
        public void TestComparison()
        {
            // Test currency comparison.
            Assert.IsTrue(MoneyCurrency.USDollar == MoneyCurrency.USDollar, "Comparision of two like currencies failed.");
            Assert.IsTrue(MoneyCurrency.Euro != MoneyCurrency.USDollar, "Comparision of two different currencies failed.");
            Assert.IsFalse(MoneyCurrency.Euro == MoneyCurrency.USDollar, "Comparision of two different currencies failed.");
            Assert.IsFalse(MoneyCurrency.USDollar != MoneyCurrency.USDollar, "Comparision of two different currencies failed.");

            Assert.IsTrue(usdZero == eurZero, "All zero values should compare to eachother as equals.");
            Assert.IsTrue(usdZero == usdZero, "All zero values should compare to eachother as equals.");
            Assert.IsTrue(usdZero == Money.Zero, "All zero values should compare to eachother as equals.");
            Assert.IsFalse(usdZero != eurZero, "All zero values should compare to eachother as equals.");
            Assert.IsFalse(usdZero != usdZero, "All zero values should compare to eachother as equals.");
            Assert.IsFalse(usdZero != Money.Zero, "All zero values should compare to eachother as equals.");

            Assert.IsTrue(usdPositive5 == usdPositive5, "Equal values of the same currency should evaluate as equal.");
            Assert.IsTrue(usdPositive5 != usdPositive15, "Inequal values of the same currency should evaluate as inequal.");
            Assert.IsFalse(usdPositive5 == eurPositive5, "Values of different currencies should not evaluate as equal.");
            Assert.IsTrue(usdPositive5 != eurPositive5, "Values of different currencies should evaluate as inequal.");

            Assert.IsTrue(usdZero < usdPositive5, "Zero is less than any positive value.");
            Assert.IsTrue(Money.Zero < usdPositive5, "Zero is less than any positive value.");
            Assert.IsTrue(eurZero < usdPositive5, "Zero is less than any positive value.");

            Assert.IsTrue(usdZero > usdNegative5, "Zero is always greater than any zero.");
            Assert.IsTrue(Money.Zero > usdNegative5, "Zero is always greater than any zero.");
            Assert.IsTrue(eurZero > usdNegative5, "Zero is always greater than any zero.");

            Assert.IsTrue(eurPositive5 > usdNegative10, "A positive is always greater than a negative (of any currency).");
            Assert.IsTrue(eurPositive5 >= usdNegative10, "A positive is always greater than a negative (of any currency).");
            Assert.IsTrue(eurNegative10 < usdPositive5, "A positive is always greater than a negative (of any currency).");
            Assert.IsTrue(eurNegative10 <= usdPositive5, "A positive is always greater than a negative (of any currency).");

            // Ensure similar values compare.
            Assert.IsTrue(usdNegative5 > usdNegative10, "Values of the same currency did not compare properly (-5>-10 != true).");
            Assert.IsTrue(usdPositive10 >= usdNegative10, "Values of the same currency did not compare properly (-5=>-10 != true).");
            Assert.IsTrue(usdPositive10 < usdPositive15, "Values of the same currency did not compare properly (10<15 != true).");
            Assert.IsTrue(usdPositive10 <= usdPositive15, "Values of the same currency did not compare properly (10<=15 != true).");

            // Negative tests (falses)
            Assert.IsFalse(usdNegative5 > usdPositive10, "Negatives are always less than positives.");
            Assert.IsFalse(usdPositive10 < usdNegative5, "Negatives are always less than positives.");
            Assert.IsFalse(usdNegative5 >= usdPositive10, "Negatives are always less than positives.");
            Assert.IsFalse(usdPositive10 <= usdNegative5, "Negatives are always less than positives.");

            // Ensure comparisons can't happen between different currencies.
            try
            {
                bool result = eurPositive5 > usdPositive10;
                Assert.Fail("Comparison between two different Money of different currency should fail.");
            }
            catch { /* This should fail.*/ }

            try
            {
                bool result = eurPositive5 >= usdPositive10;
                Assert.Fail("Comparison between two different Money of different currency should fail.");
            }
            catch { /* This should fail.*/ }

            try
            {
                bool result = eurPositive5 < usdPositive10;
                Assert.Fail("Comparison between two different Money of different currency should fail.");
            }
            catch { /* This should fail.*/ }

            try
            {
                bool result = eurPositive5 <= usdPositive10;
                Assert.Fail("Comparison between two different Money of different currency should fail.");
            }
            catch { /* This should fail.*/ }
        } 
    }
}
